use std::cell::RefCell;
use std::ptr;
use std::rc::Rc;

use ash::vk;
use ash::vk::CommandBufferUsageFlags;
use ash::vk::Offset2D;

// 
use crate::utils::constant::*;

use crate::utils::gltf_loader::GltfInput;
use crate::vulkan::vulkan_rhi::*;

use super::object::object::Drawable;
use super::object::object::Scene;
use super::object::object::Updateable;
use super::object::object::UpdateableContext;

impl VulkanRHI {
    pub fn record_command_buffer(&self, image_index: u32, command_buffer_index: usize, scene: &Scene, pre_frame_descriptor_sets: &Vec<Vec<ash::vk::DescriptorSet>>) {
        let command_buffer_begin_info = vk::CommandBufferBeginInfo {
            s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO,
            p_next: ptr::null(),
            flags: CommandBufferUsageFlags::empty(),
            p_inheritance_info: ptr::null()
        };
        
        unsafe {
            self.logic_device
                .begin_command_buffer(self.command_buffers[command_buffer_index], &command_buffer_begin_info)
                .expect("failed to begin command buffer!")    
        };

        let clear_values = [
            vk::ClearValue {
                color: vk::ClearColorValue { float32: [0.5f32, 0.5f32, 0.5f32, 0.0f32] }
            },
            vk::ClearValue {
                depth_stencil: vk::ClearDepthStencilValue{ depth: 1.0, stencil: 0}
            }
        ];

        let render_pass_begin_info = vk::RenderPassBeginInfo {
            s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO,
            p_next: ptr::null(),
            render_pass: self.render_pass,
            framebuffer: self.framebuffers[image_index as usize],
            render_area: vk::Rect2D { offset: Offset2D { x: 0, y: 0 }, extent: self.swapchain_stuff.swapchain_extent },
            clear_value_count: clear_values.len() as u32,
            p_clear_values: clear_values.as_ptr()
        };

        let viewports = [vk::Viewport {
            x: 0.0f32,
            y: 0.0f32,
            width: self.swapchain_stuff.swapchain_extent.width as f32,
            height: self.swapchain_stuff.swapchain_extent.height as f32,
            min_depth: 0.0f32,
            max_depth: 1.0f32
        }];
        let scissors = [vk::Rect2D {
            offset: Offset2D { x: 0, y: 0 },
            extent: self.swapchain_stuff.swapchain_extent
        }];
        unsafe {
            self.logic_device
                .cmd_set_viewport(self.command_buffers[command_buffer_index], 0, &viewports);
            self.logic_device
                .cmd_set_scissor(self.command_buffers[command_buffer_index], 0, &scissors);
        }

        unsafe {
            self.logic_device
                .cmd_begin_render_pass(self.command_buffers[command_buffer_index], &render_pass_begin_info, vk::SubpassContents::INLINE);
            self.logic_device
                .cmd_bind_pipeline(self.command_buffers[command_buffer_index], vk::PipelineBindPoint::GRAPHICS, self.graphics_pipelines[0]);
        }

        // View Project PreFrameUniformData
        unsafe {
            self.logic_device
                .cmd_bind_descriptor_sets(self.command_buffers[command_buffer_index], ash::vk::PipelineBindPoint::GRAPHICS, 
                self.pipeline_layout, 0, &pre_frame_descriptor_sets[command_buffer_index], &[]);
        }

        scene.draw(&self, command_buffer_index);

        unsafe {    
            self.logic_device
                .cmd_end_render_pass(self.command_buffers[command_buffer_index]);
            self.logic_device
                .end_command_buffer(self.command_buffers[command_buffer_index])
                .expect("failed to end command buffer");
        }
    }

    pub fn wait_for_fences(&self, fence_index: usize) {
        unsafe {
            self.logic_device
                .wait_for_fences(&[self.in_flight_fences[fence_index]], true, u64::MAX)
                .expect("failed to wait for fences!")
        };
    }

    pub fn reset_fences(&self, fence_index: usize) {
        unsafe {
            self.logic_device
                .reset_fences(&[self.in_flight_fences[fence_index]])
                .expect("failed to reset fences!")
        };
    }

    pub fn acquired_next_image(&mut self, semaphore_index: usize, window: &winit::window::Window) -> (u32, bool) {
        let image_index = unsafe {
            match self.swapchain_stuff.swapchain_loader
                .acquire_next_image(self.swapchain_stuff.swapchain, u64::MAX, self.image_available_semaphores[semaphore_index], vk::Fence::null()) {
                    Ok(result) => (result.0, true),
                    Err(error_code) => {
                        if error_code == vk::Result::ERROR_OUT_OF_DATE_KHR { 
                            self.recreate_swapchain(window);
                        } else {
                            panic!("failed to acquired next image!");
                        }
                        (0u32, false)
                    }
                }
        };
        image_index
    }

    pub fn reset_command_buffer(&self, command_buffer_index: usize) {
        unsafe {
            self.logic_device
                .reset_command_buffer(self.command_buffers[command_buffer_index], vk::CommandBufferResetFlags::empty())
                .expect("failed to reset command buffer!")
        };
    }

    pub fn queue_submit(&self, wait_semaphore_index: usize, use_semaphore_index: usize, use_fence_index: usize, command_buffer_index: usize) {
        let wait_semaphores = [self.image_available_semaphores[wait_semaphore_index]];
        let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];

        let signal_semaphores = [self.render_finished_semaphores[use_semaphore_index]];
        let command_buffer = [self.command_buffers[command_buffer_index]];
        let submit_info = [vk::SubmitInfo {
            s_type: vk::StructureType::SUBMIT_INFO,
            p_next: ptr::null(),
            wait_semaphore_count: wait_semaphores.len() as u32,
            p_wait_semaphores: wait_semaphores.as_ptr(),
            p_wait_dst_stage_mask: wait_stages.as_ptr(),
            command_buffer_count: command_buffer.len() as u32,
            p_command_buffers: command_buffer.as_ptr(),
            signal_semaphore_count: signal_semaphores.len() as u32,
            p_signal_semaphores: signal_semaphores.as_ptr()
        }];

        unsafe {
            self.logic_device
                .queue_submit(self.graphics_queue, &submit_info, self.in_flight_fences[use_fence_index])
                .expect("failed to queue submit");
        };
    }

    pub fn queue_present(&self, image_index: u32, use_semaphore_index: usize) -> bool {
        let signal_semaphores = [self.render_finished_semaphores[use_semaphore_index]];
        let swapchains = [self.swapchain_stuff.swapchain];

        let present_info = vk::PresentInfoKHR {
            s_type: vk::StructureType::PRESENT_INFO_KHR,
            p_next: ptr::null(),
            wait_semaphore_count: signal_semaphores.len() as u32,
            p_wait_semaphores: signal_semaphores.as_ptr(),
            swapchain_count: swapchains.len() as u32,
            p_swapchains: swapchains.as_ptr(),
            p_image_indices: &image_index,
            p_results: ptr::null_mut()
        };

        unsafe {
            match self.swapchain_stuff.swapchain_loader
                .queue_present(self.present_queue, &present_info) {
                Ok(result_bool) => result_bool,
                Err(error_code) => {
                    if error_code == vk::Result::ERROR_OUT_OF_DATE_KHR {
                        true
                    } else {
                        panic!("failed to queue present!");
                        // false
                    }
                }
            }
        }
    }

    pub fn device_wait_idle(&self) {
        unsafe {
            self.logic_device
                .device_wait_idle()
                .expect("failed to wait idle!")
        };
    }

    pub fn cleanup_swapchain(&mut self) {
        unsafe {
            // 释放 Framebuffers
            for framebuffer in self.framebuffers.iter() {
                self.logic_device.destroy_framebuffer(*framebuffer, None);
            }
            // 释放 Image Views
            for swapchain_image_view in self.swapchain_image_views.iter() {
                self.logic_device.destroy_image_view(*swapchain_image_view, None);
            }
            // 释放 SwapChain
            self.swapchain_stuff.swapchain_loader.destroy_swapchain(self.swapchain_stuff.swapchain, None);
        }
    }

    pub fn recreate_swapchain(&mut self, window: &winit::window::Window) {
        self.device_wait_idle();

        self.cleanup_swapchain();

        let swapchain_stuff = Self::create_swapchain(&self.instance, &self.surface_stuff, &self.physical_device, &self.logic_device, window);
        let swapchain_image_views = Self::create_image_views(&self.logic_device, &swapchain_stuff.swapchain_format, &swapchain_stuff.swapchain_images);
        let (depth_image, depth_image_memory, depth_image_view) = Self::create_depth_resources(&self.instance, &self.logic_device, &self.physical_device, &swapchain_stuff, 
            &self.command_pool, &self.graphics_queue);
        let framebuffers = Self::create_framebuffers(&self.logic_device, &swapchain_image_views, &self.render_pass, &swapchain_stuff, &depth_image_view);

        self.swapchain_stuff = swapchain_stuff;
        self.swapchain_image_views = swapchain_image_views;
        self.framebuffers = framebuffers;
        self.depth_image = depth_image;
        self.depth_image_memory = depth_image_memory;
        self.depth_image_view = depth_image_view;
    }

    pub fn update_uniform_buffer(&self, frame_index: usize, delta_time: f32, gltf_inputs: &Vec<Rc<RefCell<GltfInput>>>, time_duration: f32, vulkan_rhi: &VulkanRHI, scene: &mut Scene) {
        for gltf_input in gltf_inputs {
            gltf_input.borrow_mut().update_animation(delta_time);
        }

        let context = UpdateableContext {
            vulkan_rhi,
            frame_index,
            delta_time,
            time_duration
        };

        scene.update(&context);
    }
}

impl Drop for VulkanRHI {
    fn drop(&mut self) {
        unsafe {
            self.device_wait_idle();

            self.cleanup_swapchain();

            self.logic_device.destroy_image_view(self.depth_image_view, None);
            self.logic_device.destroy_image(self.depth_image, None);
            self.logic_device.free_memory(self.depth_image_memory, None);

            self.logic_device.destroy_descriptor_pool(self.descriptor_pool, None);

            self.logic_device.destroy_buffer(self.null_uniform_buffer, None);
            self.logic_device.destroy_buffer(self.null_stroage_buffer, None);
            for descriptor_set_layout in self.descriptor_set_layouts.iter() {
                self.logic_device.destroy_descriptor_set_layout(*descriptor_set_layout, None);
            }

            // 释放 同步信号
            for index in 0..MAX_FRAMES_IN_FLIGHT as usize {
                self.logic_device.destroy_semaphore(self.image_available_semaphores[index], None);
                self.logic_device.destroy_semaphore(self.render_finished_semaphores[index], None);
                self.logic_device.destroy_fence(self.in_flight_fences[index], None);
            }

            // 释放 CommandPool
            self.logic_device.destroy_command_pool(self.command_pool, None);
            // 释放 Pipeline
            for graphics_pipeline in self.graphics_pipelines.iter() {
                self.logic_device.destroy_pipeline(*graphics_pipeline, None);
            }
            // 释放 Render Pass
            self.logic_device.destroy_render_pass(self.render_pass, None);
            // 释放 pipeline layout
            self.logic_device.destroy_pipeline_layout(self.pipeline_layout, None);
            // 释放 Surface
            self.surface_stuff.surface_loader.destroy_surface(self.surface_stuff.surface, None);
            // 释放 Logic device, 注意注销顺序
            self.logic_device.destroy_device(None);
            // 释放 Validation
            if ENABLE_VALIDATION {
                self.debug_utils_loader.destroy_debug_utils_messenger(
                    self.debug_messenger, 
                    None
                );
            }
            // 释放 Instance
            self.instance.destroy_instance(None);
        }
    }
}